#include "stdafx.h"
#include "QueueThread.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
int _SEHFilter(DWORD dwExceptionCode)
{
    switch (dwExceptionCode)
    {
    case EXCEPTION_ACCESS_VIOLATION:
        return EXCEPTION_EXECUTE_HANDLER;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
QueueThread::QueueThread()
{
	m_lpThread = NULL;
	m_hEndEvent = NULL;
	m_hQueue = NULL;
	m_hQueue = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hEndEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	InitializeCriticalSection(&m_CriticalSection);
	
	StartThread();
}

QueueThread::~QueueThread()
{
	EndThread();

	if(m_hEndEvent != NULL)
	{
		CloseHandle(m_hEndEvent);
		m_hEndEvent = NULL;
	}

	if(m_hQueue != NULL)
	{
		CloseHandle(m_hQueue);
		m_hQueue = NULL;
	}
	
	DeleteCriticalSection(&m_CriticalSection);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
BOOL QueueThread::StartThread()
{
	if((m_lpThread != NULL) || (m_hEndEvent == NULL) || (m_hQueue == NULL))
	{
		return FALSE;
	}
	
	m_lpQueueThread = this;

	m_lpThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)WorkerThread, m_lpQueueThread, 0, 0);

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
void QueueThread::EndThread()
{
	if(m_hEndEvent != NULL)
	{
		SetEvent(m_hEndEvent);
		
		if(m_lpThread != NULL)
		{
			WaitForSingleObject(m_lpThread, 5000/*INFINITE*/);
			::TerminateThread(m_lpThread, 0);
			CloseHandle(m_lpThread);
		}
		
		CloseHandle(m_hEndEvent);
		
		m_hEndEvent = NULL;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
UINT QueueThread::WorkerThread(void *lpParam)
{
	QueueThread *lpQueueThread = (QueueThread*)lpParam;
	return lpQueueThread->Run();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
UINT QueueThread::Run()
{
	DWORD dwRet;
	HANDLE hEvent[2] = {m_hEndEvent, m_hQueue};

	while(true)
	{
		dwRet = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE);
		switch(dwRet)
		{
		case WAIT_OBJECT_0:		// Thread terminate request
			{
				return 0;
			}
			break;
		case WAIT_OBJECT_0+1:	
			{
				QueueParam queueParam;
				while(GetQueueMessage(queueParam))
				{
					ProcessQueue(queueParam);
					if(CheckEndRequest() == WAIT_OBJECT_0)
						return 0;
				}
			}
		case WAIT_FAILED:
			break;
		default:
			break;
		}
	}
	return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
int QueueThread::AddQueueMessage(ULONGLONG ullParam_1, ULONGLONG ullParam_2, ULONGLONG ullParam_3)
{
	int nRet = 1;

	EnterCriticalSection(&m_CriticalSection);

	QueueParam *lpQueueParam = new QueueParam;
	
	lpQueueParam->m_ullParam_1 = ullParam_1 ;
	lpQueueParam->m_ullParam_2 = ullParam_2 ;
	lpQueueParam->m_ullParam_3 = ullParam_3;
	
	m_Queue.push_back(lpQueueParam);

	SetEvent(m_hQueue);

	LeaveCriticalSection(&m_CriticalSection);

	return nRet;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
int QueueThread::GetQueueMessage(QueueParam& queueParam)
{
	int nRet = 1;
	bool bRet = false;

	EnterCriticalSection(&m_CriticalSection);
	if(m_Queue.size() == 0)
	{
		ResetEvent(m_hQueue);
		nRet = 0;
	}
	else
	{
		QueueParam *lpQueueParam = (QueueParam*)m_Queue.front();
		
		m_Queue.pop_front();

		queueParam.m_ullParam_1 = lpQueueParam->m_ullParam_1;
		queueParam.m_ullParam_2 = lpQueueParam->m_ullParam_2;
		queueParam.m_ullParam_3 = lpQueueParam->m_ullParam_3;

		delete lpQueueParam;
		
		nRet = 1;
	}
	LeaveCriticalSection(&m_CriticalSection);

	return nRet;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
void QueueThread::ProcessQueue(QueueParam queueParam)
{	
	ULONGLONG ullParam_1 = queueParam.m_ullParam_1 ;
	ULONGLONG ullParam_2 = queueParam.m_ullParam_2 ;
	ULONGLONG ullParam_3 = queueParam.m_ullParam_3;

	__try
	{
		HandleMessage(ullParam_1, ullParam_2, ullParam_3);
	}
	__except( _SEHFilter(GetExceptionCode()))
	{
		// TODO LOG
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
void QueueThread::HandleMessage(ULONGLONG ullParam_1, ULONGLONG ullParam_2, ULONGLONG ullParam_3)
{

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
DWORD QueueThread::CheckEndRequest()
{
	return WaitForSingleObject(m_hEndEvent, 0);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
void QueueThread::ClearQueue()
{
	QueueParam queueParam;
	while (GetQueueMessage(queueParam));
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
int QueueThread::GetQueueSize()
{
	int counts = 0;

	EnterCriticalSection(&m_CriticalSection);
	counts = m_Queue.size();
	LeaveCriticalSection(&m_CriticalSection);

	return counts;
}